/**
 * 
 */
package org.metaverse.h2.sql;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.vocabulary.XMLSchema;

/**
 * @author vjache
 *
 */
public class DbValueFormat {
	private static final  Map<URI, Integer> LIT_TYPE_TO_INT = new HashMap<URI, Integer>();
	private static final  Map<Integer,URI> INT_TO_LIT_TYPE = new HashMap<Integer,URI>();
	static
	{
		int idx = 1000;
		LIT_TYPE_TO_INT.put(XMLSchema.ANYURI, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.STRING, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.INTEGER, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.LONG, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.INT, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.BOOLEAN, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.BYTE, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.SHORT, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.FLOAT, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.DOUBLE, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.DECIMAL, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.DATETIME, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.DATE, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.GDAY, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.GMONTH, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.GMONTHDAY, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.GYEAR, idx++);
		LIT_TYPE_TO_INT.put(XMLSchema.GYEARMONTH, idx++);
		
		for(Entry<URI, Integer> ent:LIT_TYPE_TO_INT.entrySet())
			INT_TO_LIT_TYPE.put(ent.getValue(), ent.getKey());
	}
	
	public static class ValueStruct
	{
		public String _val;
		public int _typ;
	}

	/**
	 * 
	 */
	private DbValueFormat() {}
	
	public static ValueStruct parseValue(Value val) {
		ValueStruct vs = new ValueStruct();
		parseValue(val, vs);
		return vs;
	}
	
	public static void parseValue(Value val, ValueStruct valStruct) {
		if(val == null)
			throw new NullPointerException("Value must not be null");
		if(val instanceof URI)
		{
			valStruct._val = val.stringValue();
			valStruct._typ = 0;
		}
		else if(val instanceof Literal)
		{
			valStruct._val = val.stringValue();
			final Literal lit = (Literal)val;
			final URI datatype = lit.getDatatype();
			if(datatype != null)
				valStruct._typ = literalDatatypeToInt(datatype);
			else
			{
				final String language = lit.getLanguage();
				if(language==null || language.length()==0)
				{
					valStruct._typ = 2;//Plain literal
				}
				else
					valStruct._typ = literalLanguageToInt(language);
			}
		}
		else if(val instanceof BNode)
		{
			valStruct._val = val.stringValue();
			valStruct._typ = 1;
		}
		else
			throw new IllegalArgumentException(String.format("Unsupported node class '%s'.",val.getClass()));
	}

	private static int literalLanguageToInt(String language) {
		String ll = language.toLowerCase();
		if("en".equals(ll))
		{
			return 10;
		}
		else if("ru".equals(ll))
		{
			return 11;
		}
		throw new IllegalArgumentException(String.format("Unsupported language '%s' encountered.",ll));
	}

	private static int literalDatatypeToInt(URI datatype) {
		Integer integer = LIT_TYPE_TO_INT.get(datatype);
		if(integer == null)
			throw new IllegalArgumentException(String.format("Unsupported Literal type <%s>.",datatype));
		return integer;
	}

	public static Value restoreValue(ValueStruct vs, ValueFactory aVF) {
		if(0 == vs._typ)//URIRef
		{
			return aVF.createURI(vs._val);
		}
		else if(1 == vs._typ)//Blank
		{
			return aVF.createBNode(vs._val);
		}
		else if(2 == vs._typ)//Plain literal
		{
			return aVF.createLiteral(vs._val);
		}
		else if(10 <= vs._typ && vs._typ<1000)//Literal with i18n
		{
			switch(vs._typ)
			{
			case 10: return aVF.createLiteral(vs._val,"en");
			case 11: return aVF.createLiteral(vs._val,"ru");
			default:
				throw new IllegalArgumentException(String.format("Language with type '%s' not supported.",vs._typ));
			}
		}
		else if(vs._typ>=1000)//Literal with datatype
		{
			final URI dt = INT_TO_LIT_TYPE.get(vs._typ);
			if(dt!=null)
				return aVF.createLiteral(vs._val,dt);
		}
		
		throw new IllegalArgumentException(String.format("Unexpected value type code '%s'. Value '%s'.", vs._typ,vs._val));
	}

}
